
#ifndef INFERENCE_DATUM_H
#define INFERENCE_DATUM_H
#include <stdint.h>
#include <iostream>
#include <vector>

enum PadType { ZERO = 0,
               CONSTANT };
enum DIM {
    DIMN = 0,  // n
    DIMC,      // c
    DIMH,      // h
    DIMW       // w

};

template <class Datatype>
class Tensor {
   public:
    Tensor();

    Tensor(size_t len);

    Tensor(size_t n, size_t h, size_t w, size_t c);

    Tensor(size_t n, size_t h, size_t w, size_t c, Datatype d);

    Tensor(const Datatype* data_ptr, size_t _n, size_t _h, size_t _w, size_t _c);

    Tensor(const std::vector<Datatype>& data,
           const std::vector<uint32_t>& shapes);

    //   Tensor(const vector<int>& data, const std::vector<Datatype>& shapes);

    ~Tensor();

    Datatype* getData() const;

    Datatype* getData();

    std::vector<Datatype>& getDataA();

    void setDataA(std::vector<Datatype>);

    bool empty();

    size_t len() const;

    void getSize(uint32_t* size) const;
    /*
           为tensor扩容，使其能够容纳更多数据
           若_n * _h * _w * _c > 现有空间，则扩容
           否则，不改变tensor空间大小
    */
    void setSize(size_t _n, size_t _h, size_t _w, size_t _c);
    /*
    在不改变tensor数据总量和数据排列的情况下，重新设置tensor的各个维度
    e.g. 一个tensor尺度为（1，4，6，2）
                  0  2  4  6  8  10 | 1  3  5  7  9  11
                  12 14 16 18 20 22 | 13 15 17 19 21 23
                  24 26 28 30 32 34 | 25 27 29 31 33 35
                  36 38 40 42 44 46 | 37 39 41 43 45 47
        reSize（1，4，3，4）
                  0  4  8 | 1  5  9 | 2  6  10 | 3  7  11
                  12 16 20| 13 17 21| 14 18 22 | 15 19 23
                  24 28 32| 25 29 33| 26 30 32 | 27 31 35
                  36 30 44| 37 41 45| 38 42 44 | 39 43 47
    */
    void reSize(size_t _n, size_t _h, size_t _w, size_t _c);

    std::vector<Datatype> changeDim();

    const std::vector<uint32_t> getSize() const;  // cnn_new_add

    Tensor& operator+(const Tensor& d);  // Add to self

    Tensor operator+(const Tensor& d) const;  // new tensor

    Tensor operator-(const Tensor& d) const;

    Tensor operator*(const Tensor& d) const;  // Entrywise

    Tensor operator*(const Datatype s);  // Entrywise

    Tensor operator/(const Tensor& d) const;

    Tensor operator=(const Tensor& d);

    const Datatype& at(size_t index) const;

    Datatype& at(const size_t index);

    const Tensor operator[](size_t i) const;

    void matMulNew(const Tensor& m1, const Tensor& m2);

    void matMul(const Tensor& w1, const Tensor& w2);

    void matMulOmp(const Tensor& w1, const Tensor& w2);

    Tensor Mean(DIM dim, bool BroadCast = true) const;

    Tensor Pow(int ex) const;

    Tensor Transpose(int dim1, int dim2);

    Tensor Transpose(int dim1, int dim2) const;

    Tensor Permute(const std::vector<int>& axes) const;

    void PermuteSelf(const std::vector<int>& axes);  // permute to self

    Tensor Reshape(int _n, int _h, int _w, int _c) const;

    Tensor Concat(const Tensor& in, DIM dim) const;

    Tensor Concat(const Tensor& tensor1, const Tensor& tensor2) const;

    std::vector<Tensor> Split(int splitSize);

    Tensor Softmax(DIM dim) const;

    void fill(Datatype);

    void fill(const std::vector<Datatype>& data);

    void fill(const Datatype* inData, const unsigned dataLen);
    /*
    在矩形四周补零。pU在H维度上补零， pD在H维度下补零，pL在W维度上补零,
    pR在W维度上补零. e.g. 一个矩形的尺寸为（5，6）,pU = 1, pD = 2, pL = 3, pR = 4
    原始矩形为：
                            ******
                            ******
                            ******
                            ******
                            ******
    pad之后的矩形为：
                        0000000000000
                        000******0000
                        000******0000
                        000******0000
                        000******0000
                        000******0000
                        0000000000000
                        0000000000000
    */
    Tensor& pad(size_t pU, size_t pD, size_t pL, size_t pR);

    Tensor& im2col(const std::vector<uint32_t>& t, uint32_t sh, uint32_t sw);

    void fromString(std::string& content);

    std::string toString();

    Tensor& slice(const Tensor& t, uint32_t start);

    Tensor& clip(const Datatype min, const Datatype max);
    void clip(const Tensor& t, const Datatype min, const Datatype max);

    void reshape9x9x3();
    void reshape6x6x3();
    void reshape3x3x12(int operate = 1);

   private:
    std::vector<Datatype> data;
    size_t n = 0;
    size_t h = 0;
    size_t w = 0;
    size_t c = 0;
    void Stringsplit(const std::string& str, const char split, std::vector<std::string>& rst);
};
#include "tensor.hpp"
#endif
